#include <jni.h>
#include "LogUtil.h"
#include "glmUtils.h"
#include "render/MyRender.h"
#include "ImageDef.h"

#define NATIVE_RENDER_CLASS_NAME "com/yyl/render/render/YYLRender"
#ifdef __cplusplus
extern "C" {
#endif
extern "C"

JNIEXPORT void JNICALL nativeOnDestroy(JNIEnv *env, jobject instance) {
    MyRender::DestroyInstance();
}


JNIEXPORT void JNICALL nativeSetProgramType(JNIEnv *env, jobject instance, jint type) {
    MyRender::GetInstance()->SetProgramType(type);
}
JNIEXPORT void JNICALL nativeLoadShaderScript(JNIEnv *env, jobject thiz, jint type,
                                              jstring script_string) {
    int length = env->GetStringUTFLength(script_string);
    const char *cStr = env->GetStringUTFChars(script_string, JNI_FALSE);
    char *buf = static_cast<char *>(malloc(length + 1));
    memcpy(buf, cStr, length + 1);
    MyRender::GetInstance()->LoadFragShaderScript(type, buf, length + 1);
    free(buf);
    env->ReleaseStringUTFChars(script_string, cStr);
}
JNIEXPORT void JNICALL nativeLoadFilterData
        (JNIEnv *env, jobject instance, jint index, jint format, jint width, jint height,
         jbyteArray imageData) {
    int len = env->GetArrayLength(imageData);
    uint8_t *buf = new uint8_t[len];
    env->GetByteArrayRegion(imageData, 0, len, reinterpret_cast<jbyte *>(buf));
//    ByteFlowRenderContext *pContext = ByteFlowRenderContext::GetRenderContext(env, instance);
//    if(pContext) pContext->LoadLutImageData(index, format, width, height, buf);
    delete[] buf;
    env->DeleteLocalRef(imageData);
}
JNIEXPORT void JNICALL nativeSetTransformMatrix
        (JNIEnv *env, jobject instance, jfloat translateX, jfloat translateY, jfloat scaleX,
         jfloat scaleY, jint degree, jint mirror) {
    MyRender::GetInstance()->SetTransformMatrix(translateX, translateY, scaleX, scaleY, degree,
                                                mirror);
}
JNIEXPORT void JNICALL
nativeUpdateFrame(JNIEnv *env, jobject instance, jint format, jint width,
                  jint height, jbyteArray bytes) {
    int len = env->GetArrayLength(bytes);
    unsigned char *buf = new unsigned char[len];
    env->GetByteArrayRegion(bytes, 0, len, reinterpret_cast<jbyte *>(buf));
    MyRender::GetInstance()->UpdateFrame(format, width, height, buf);
    delete[] buf;
}

JNIEXPORT void JNICALL nativeOnSurfaceCreated(JNIEnv *env, jobject instance) {
//    auto address = static_cast<void *>(render);
//    LOGI("SurfaceCreated address=%p", address);
    MyRender::GetInstance()->OnSurfaceCreated();
}
JNIEXPORT void JNICALL nativeOnSurfaceChanged
        (JNIEnv *env, jobject instance, jint width, jint height) {
    MyRender::GetInstance()->OnSurfaceChanged(width, height);
}
JNIEXPORT void JNICALL nativeOnSurfaceDrawFrame(JNIEnv *env, jobject instance) {
    MyRender::GetInstance()->OnSurfaceDrawFrame();

}
JNIEXPORT void JNICALL nativeOnSurfaceDestroy(JNIEnv *env, jobject instance) {
    MyRender::GetInstance()->OnSurfaceDestroy();
}
#ifdef __cplusplus
}
#endif
static JNINativeMethod g_RenderMethods[] = {
        {"nativeOnDestroy",          "()V",                    (void *) (nativeOnDestroy)},
        {"nativeSetProgramType",     "(I)V",                   (void *) (nativeSetProgramType)},
        {"nativeLoadShaderScript",   "(ILjava/lang/String;)V", (void *) (nativeLoadShaderScript)},
        {"nativeUpdateFrame",        "(III[B)V",               (void *) (nativeUpdateFrame)},
        {"nativeLoadFilterData",     "(IIII[B)V",              (void *) (nativeLoadFilterData)},
        {"nativeSetTransformMatrix", "(FFFFII)V",              (void *) (nativeSetTransformMatrix)},
        {"nativeOnSurfaceCreated",   "()V",                    (void *) (nativeOnSurfaceCreated)},
        {"nativeOnSurfaceChanged",   "(II)V",                  (void *) (nativeOnSurfaceChanged)},
        {"nativeOnSurfaceDrawFrame", "()V",                    (void *) (nativeOnSurfaceDrawFrame)},
        {"nativeOnSurfaceDestroy",   "()V",                    (void *) (nativeOnSurfaceDestroy)},
};

static int
RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum) {
    LOGCATE("RegisterNativeMethods");
    jclass clazz = env->FindClass(className);
    if (clazz == NULL) {
        LOGCATE("RegisterNativeMethods fail. clazz == NULL");
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, methods, methodNum) < 0) {
        LOGCATE("RegisterNativeMethods fail");
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

static void UnregisterNativeMethods(JNIEnv *env, const char *className) {
    LOGCATE("UnregisterNativeMethods");
    jclass clazz = env->FindClass(className);
    if (clazz == NULL) {
        LOGCATE("UnregisterNativeMethods fail. clazz == NULL");
        return;
    }
    if (env != NULL) {
        env->UnregisterNatives(clazz);
    }
}
// call this func when loading lib
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *jvm, void *p) {
    LOGCATE("===== JNI_OnLoad =====");
    jint jniRet = JNI_ERR;
    JNIEnv *env = NULL;
    if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
        return jniRet;
    }

    jint regRet = RegisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME, g_RenderMethods,
                                        sizeof(g_RenderMethods) / sizeof(g_RenderMethods[0]));
    if (regRet != JNI_TRUE) {
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT void JNI_OnUnload(JavaVM *jvm, void *p) {
    JNIEnv *env = NULL;
    if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK) {
        return;
    }
    UnregisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME);
}


